{
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python37564bit6afae4a42a5941c0967cdcfc2650559a",
   "display_name": "Python 3.7.5 64-bit"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "source": [
    "# 量子神经网络初体验\n",
    "\n",
    "`Linux` `CPU` `全流程` `初级` `中级` `高级`\n",
    "\n",
    "[![](https://gitee.com/mindspore/docs/raw/master/resource/_static/logo_source.png)](https://gitee.com/mindspore/docs/blob/master/docs/mindquantum/docs/source_zh_cn/parameterized_quantum_circuit.ipynb) [![](https://gitee.com/mindspore/mindquantum/raw/master/tutorials/images/view_mindquantum_api.png)](https://mindspore.cn/mindquantum/api/zh-CN/master/index.html)\n",
    "\n",
    "## 1. 量子神经网络的结构\n",
    "\n",
    "在MindQuantum中，量子神经网络（Quantum Neural Network, QNN）的结构如下图所示，其通常由三部分构成：\n",
    "\n",
    "（1）一个（或多个）编码线路，用于将经典数据编码到量子数据（通常称为Encoder）；\n",
    "\n",
    "（2）一个（或多个）训练线路，用于训练带参量子门中的参数（通常称为Ansatz）；\n",
    "\n",
    "（3）一个（或多个）测量，用于检测测量值（例如在`Z`方向上测量，就是某个量子比特的量子态在`Z`轴上的投影，该测量得到的是量子态关于泡利`Z`算符（不限定于泡利`Z`算符，换成其它的算符亦可）的期望值）是否接近于目标期望值。\n",
    "\n",
    "![](https://gitee.com/mindspore/mindquantum/raw/master/tutorials/images/mindquantum.png)\n",
    "\n",
    "\n",
    "下面，我们通过一个简单的例子来体验一下如何使用MindQuantum。\n",
    "\n",
    "\n",
    "## 2. 简单的例子\n",
    "\n",
    "![](https://gitee.com/mindspore/mindquantum/raw/master/tutorials/images/example_circuit.png)\n",
    "\n",
    "我们搭建如上图所示的量子神经网络，其中Encoder由一个`H`门，1个`RX`门、1个`RY`门和1个`RZ`门构成，Ansatz由1个`RX`门和1个`RY`门构成，测量则是作用在第0位量子比特上的泡利`Z`算符。\n",
    "\n",
    "\n",
    "\n",
    "问题描述：我们将Encoder看成是系统对初始量子态的误差影响（参数$\\alpha_0, \\alpha_1$​和$\\alpha_2$​是将原经典数据经过预处理（可选）后得到的某个固定值，即为已知值，在此分别设为0.2, 0.3和0.4）。我们需要训练一个Ansatz来抵消掉这个误差，使得最后的量子态还是处于$|0\\rangle$​态。\n",
    "\n",
    "\n",
    "\n",
    "思路：对末态执行泡利`Z`算符测量，此时的测量值就是此时的量子态关于泡利`Z`算符的期望值。由于$|0\\rangle$​​是算符`Z`的本征态，且本征值为1，容易知道\n",
    "$$\n",
    "\\langle 0|Z|0\\rangle=1.\n",
    "$$\n",
    "也就是说，目标期望值为1。可以通过测量得到的期望值来验证此时的状态是否为$|0\\rangle$。\n",
    "\n",
    "\n",
    "解决方案：通过训练Ansatz中的参数，希望测量值接近于目标期望值，换句话说，我们只需让测量值尽可能接近于$|0\\rangle$态关于泡利`Z`算符对应的期望值，那么此时的状态就是$|0\\rangle$，即Ansatz抵消了Encoder对初始量子态产生的误差。\n",
    "\n",
    "#### 2.1 环境准备\n",
    "\n",
    "导入本教程所依赖的模块"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np                            #导入numpy库并简写为np\n",
    "import mindquantum as mq                      #导入mindquantum库并简写为mq\n",
    "from mindquantum import Circuit               #从mindquantum库导入Circuit模块，搭建量子线路\n",
    "from mindquantum.gate import H, RX, RY, RZ    #从mindquantum.gate模块中导入量子门H, RX, RY, RZ"
   ]
  },
  {
   "source": [
    "#### 2.2 搭建Encoder\n",
    "\n",
    "根据图示的量子线路图，我们可以在MindQuantum中搭建Encoder。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "==================Circuit Summary==================\n|Total number of gates  : 4.                      |\n|Parameter gates        : 3.                      |\n|with 3 parameters are  : alpha0, alpha1, alpha2. |\n|Number qubit of circuit: 1                       |\n===================================================\n"
    }
   ],
   "source": [
    "encoder = Circuit()                   #初始化量子线路\n",
    "encoder += H.on(0)                    #H门作用在第0位量子比特\n",
    "encoder += RX(f'alpha{0}').on(0)      #RX(alpha_0)门作用在第0位量子比特\n",
    "encoder += RY(f'alpha{1}').on(0)      #RY(alpha_1)门作用在第0位量子比特\n",
    "encoder += RZ(f'alpha{2}').on(0)      #RZ(alpha_2)门作用在第0位量子比特\n",
    "encoder = encoder.no_grad()           #Encoder作为整个量子神经网络的第一层，不用对编码线路中的梯度求导数，因此加入no_grad()\n",
    "encoder.summary()                     #总结Encoder"
   ]
  },
  {
   "source": [
    "从对Encoder的Summary中可以看到，该量子线路由4个量子门组成，其中有3个含参量子门且参数为$\\alpha_0,\\alpha_1,\\alpha_2$​​​​，该量子线路调控的量子比特数为1。\n",
    "\n",
    "\n",
    "然后，我们需要对Encoder中的参数进行赋值。由于Encoder中的参数$\\alpha_0, \\alpha_1$​和$\\alpha_2$​分别为已知值0.2, 0.3和0.4，因此可以直接对参数进行赋值，并打印此时的状态。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "(0.566990315914154-0.17539066076278687j)¦0⟩\n(0.8008146286010742+0.08034947514533997j)¦1⟩\n"
    }
   ],
   "source": [
    "from mindquantum.circuit import StateEvolution      #从mindquantum.circuit模块中导入StateEvolution模块，演化量子线路，计算末态\n",
    "\n",
    "alpha0, alpha1, alpha2 = 0.2, 0.3, 0.4              #alpha0, alpha1, alpha2为已知的固定值，分别赋值0.2, 0.3 和0.4\n",
    "\n",
    "state = (StateEvolution(encoder)).final_state({'alpha0': alpha0, 'alpha1': alpha1,'alpha2': alpha2}, ket=True)                                                                             #量子线路中的参数必须赋值，ket=True表示是否展示为量子态                                \n",
    "print(state)"
   ]
  },
  {
   "source": [
    "上述步骤为了展示MindQuantum可以演化量子线路（若量子线路中的量子门带参数，则需要对参数赋值）并得到演化后的末态。从上述打印可以看到，演化后得到的末态为$|0\\rangle$​​​和$|1\\rangle$​​​组成的叠加态，各项对应的振幅为上述打印的状态左边对应的数值。  \n",
    "\n",
    "说明：\n",
    "\n",
    "（1）StateEvolution模块用于演化量子线路，计算末态，一般格式如下：StateEvolution(circuit)，括号中的circuit就是我们搭建的需要演化的量子线路，在上面的例子中，此时的Encoder我们上述的量子线路；\n",
    "\n",
    "（2）final_state模块用于展现初态经过给定的量子线路（即明确参数）后的末态，一般格式如下：final_state(param=None, ket=False)，线路中的参数param必须要明确给出，ket=True表示是否展示量子态；\n",
    "\n",
    "#### 2.3 搭建Ansatz\n",
    "\n",
    "同样地，我们也可以在MindQuantum中搭建Ansatz。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "==============Circuit Summary==============\n|Total number of gates  : 2.              |\n|Parameter gates        : 2.              |\n|with 2 parameters are  : theta1, theta0. |\n|Number qubit of circuit: 1               |\n===========================================\n"
    }
   ],
   "source": [
    "ansatz = Circuit()                           #初始化量子线路\n",
    "ansatz += RX(f'theta{0}').on(0)              #RX(theta_0)门作用在第0位量子比特\n",
    "ansatz += RY(f'theta{1}').on(0)              #RY(theta_1)门作用在第0位量子比特\n",
    "ansatz.summary()                             #总结Ansatz量子线路"
   ]
  },
  {
   "source": [
    "从对Ansatz的Summary中可以看到，该量子线路由2个量子门组成，其中有2个含参量子门且参数为$\\theta_0,\\theta_1$​​，该量子线路调控的量子比特数为1。\n",
    "\n",
    "然后，对Ansatz中的参数进行赋值。由于Ansatz为需要训练的量子线路，因此Ansatz中的参数$\\theta_0$​​和$\\theta_1$​​可以随机设定，通常默认设为初始值0。我们同样可以打印此时的量子态，不过这并不是必要的步骤，只是为了再次熟悉一下如何使用StateEvolution模块中的final_state。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "1.0¦0⟩\n"
    }
   ],
   "source": [
    "theta0, theta1 = 0, 0                        #对theta0, theta1进行赋值，设为初始值0 , 0\n",
    "\n",
    "state = (StateEvolution(ansatz)).final_state({'theta0': theta0, 'theta1': theta1}, ket = True)                                                                       \n",
    "print(state)"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，此时的状态为$|0\\rangle$​​且振幅为1。这是因为对于Ansatz来说，默认的输入量子态为$|0\\rangle$​​，而且其中的参数$\\theta_0$​​和$\\theta_1$​​都为0，此时的`RX(0)`门和`RY(0)`门都相当于`I`门，因此整个线路演化的过程就是$|0\\rangle$​​经过$I\\cdot I$，那么最后输出的态当然就是$|0\\rangle$​​​了。\n",
    "\n",
    "那么完整的量子线路就是Encoder加上Ansatz。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "==========================Circuit Summary==========================\n|Total number of gates  : 6.                                      |\n|Parameter gates        : 5.                                      |\n|with 5 parameters are  : alpha1, theta1, alpha0, alpha2, theta0. |\n|Number qubit of circuit: 1                                       |\n===================================================================\n"
    }
   ],
   "source": [
    "circuit = encoder + ansatz                   #完整的量子线路由Encoder和Ansatz组成\n",
    "circuit.summary()"
   ]
  },
  {
   "source": [
    "从对完整的量子线路的Summary中可以看到，该量子线路由6个量子门组成，其中有5个含参量子门且参数为$\\alpha_0,\\alpha_1,\\alpha_2,\\theta_0,\\theta_1$​​​，该量子线路调控的量子比特数为1。\n",
    "\n",
    "#### 2.4 构建哈密顿量\n",
    "\n",
    "我们对第0位量子比特执行泡利`Z`算符测量，构建对应的哈密顿量。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "-1 [Z0] \n"
    }
   ],
   "source": [
    "from mindquantum.ops import QubitOperator           #从mindquantum.ops模块导入QubitOperator模块，构造泡利算符\n",
    "from mindquantum.gate import Hamiltonian            #从mindquantum.gate模块导入Hamiltonian模块，构建哈密顿量\n",
    "\n",
    "ham = Hamiltonian(QubitOperator('Z0',-1))           #对第0位量子比特执行泡利Z算符测量，且将系数设置为-1，构建对应的哈密顿量\n",
    "print(ham)"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，此时构建的哈密顿量为对第0位量子比特执行泡利`Z`算符测量，且系数为-1。之所以将系数设为-1，是因为在量子神经网络的训练中，Ansatz中的参数的梯度会一直下降，同时测量值也会一直减少。如果最后收敛于-1，那么此时对应的量子态是$|1\\rangle$而不是$|0\\rangle$​，如下所示\n",
    "$$\n",
    "\\langle 1|Z|1\\rangle=-1.\n",
    "$$\n",
    "而我们所希望得到的是$|0\\rangle$态。所以，将系数设为-1，那么当测量值为-1时，此时对应的量子态就是$|0\\rangle$态，如下所示\n",
    "$$\n",
    "\\langle 0|(-Z)|0\\rangle=-1.\n",
    "$$\n",
    "\n",
    "\n",
    "说明：\n",
    "\n",
    "（1）QubitOperator是作用于量子比特的算子的总和，主要用于构造泡利算符；一般格式如下：QubitOperator(term=None, coefficient=1.0)；\n",
    "\n",
    "（2）Hamiltonian是哈密顿量包装器，主要用于构建哈密顿量，一般格式如下：Hamiltonian(QubitOperator('X0 Y2', 0.5))，X0和Y2表示泡利`X`算符作用在第0位量子比特，泡利`Y`算符作用在第2位量子比特，系数为0.5；\n",
    "\n",
    "#### 2.5 生成参数化量子线路模拟算子\n",
    "\n",
    "对于上述搭建的量子线路，我们可以在MindQuantum生成一个参数化量子线路模拟算子对其进行模拟。\n",
    "\n",
    "\n",
    "\n",
    "首先，为了方便，我们对Encoder和Ansatz中的参数数组分别命名为encoder_names和ansatz_names。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "encoder_names =  ['alpha0', 'alpha1', 'alpha2'] \nansatz_names = ['theta0', 'theta1']\n"
    }
   ],
   "source": [
    "encoder_names = encoder.para_name                   #Encoder中所有参数组成的数组，encoder.para_name系统会自动生成\n",
    "ansatz_names = ansatz.para_name                     #Ansatz中所有参数组成的数组，ansatz.para_name系统会自动生成\n",
    "print('encoder_names = ', encoder.para_name, '\\nansatz_names =', ansatz.para_name)"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，encoder_names为encoder中所有参数$\\alpha_0, \\alpha_1, \\alpha_2$​组成的数组，ansatz_names为ansatz中所有参数$\\theta_0,\\theta_1$​组成的数组，这两个数组会在生成参数化量子线路模拟算子时用到。\n",
    "\n",
    "\n",
    "然后，我们通过模块generate_pqc_operator生成一个参数化量子线路模拟算子。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "Measurement result:  [[0.29552022]]\nGradient of encoder parameters:  [[[0. 0. 0.]]]\nGradient of ansatz parameters:  [[[-0.37202555  0.87992316]]]\n"
    }
   ],
   "source": [
    "from mindquantum.nn import generate_pqc_operator                            #从mindquantum.nn模块中（nn表示神经网络）导入generate_pqc_operator模块\n",
    "\n",
    "pqc = generate_pqc_operator(encoder_names, ansatz_names, circuit, ham)      #模块generate_pqc_operator可生成参数化量子线路模拟算子\n",
    "\n",
    "import mindspore as ms                                                      #导入mindspore库并简写为ms\n",
    "from mindspore import Tensor                                                #从mindspore库导入导入Tensor模块，用于数据储存\n",
    "from mindspore import context                                               #从mindspore库导入导入context模块，用于配置当前运行环境\n",
    "context.set_context(mode=context.GRAPH_MODE, device_target=\"CPU\")           #模式：搭建静态训练图；要运行的目标设备：CPU（目前量子-经典混合神经网络只支持CPU的运行模式）\n",
    "\n",
    "encoder_data = Tensor(np.array([[alpha0,alpha1,alpha2]]).astype(np.float32))#Encoder中的alpha0, alpha1, alpha2这三个参数组成的数组，将其数据类型转换为float32，并利用Tensor储存在encoder_data中\n",
    "                                                                            #MindQuantum支持多样本的batch训练，Encoder数组是两个维度，第一个维度为样本，第二个维度为特征（即参数）\n",
    "\n",
    "ansatz_data = Tensor(np.array([theta0,theta1]).astype(np.float32))          #Ansatz中的theta0, theta1这两个参数组成的数组，将其数据类型转换为float32，并利用Tensor储存在ansatzr_data中，Ansatz数据只有一个维度，特征（即参数）\n",
    "\n",
    "measure_result, encoder_grad, ansatz_grad = pqc(encoder_data, ansatz_data)  #根据Encoder和Ansatz的数据，输出参数化量子线路的测量值，Encoder中的参数的导数和Ansatz中的参数的导数\n",
    "\n",
    "print('Measurement result: ', measure_result.asnumpy())\n",
    "print('Gradient of encoder parameters: ', encoder_grad.asnumpy())\n",
    "print('Gradient of ansatz parameters: ', ansatz_grad.asnumpy())"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，测量结果（期望值）为2.9552022，Encoder中的3个参数的导数为0，0，0（因为我们对Encoder设置了no_grad()），Ansatz中的2个参数的导数为-0.37202555，-0.87992316。\n",
    "\n",
    "这里通过generate_pqc_operator产生的只是一个算子，还不能进行训练，要把它放到量子神经网络里面才能进行训练。通过训练Ansatz中的参数，可以使得Ansatz中的参数的导数一直下降并接近于0，那么测量值也就会接近于-1。\n",
    "\n",
    "说明：\n",
    "\n",
    "（1）generate_pqc_operator模块用于生成参数化量子线路来模拟算子，一般格式如下：mindquantum.nn.generate_pqc_operator(encoder_params_names, ansatz_params_names, circuit, measurements, n_threads=1)，通常circuit为我们搭建的Encoder和Ansatz，measurements为我们构建的哈密顿量ham，n_threads为用于数据并行的线程数，默认值：1；\n",
    "\n",
    "（2）mindspore是一个全场景深度学习框架，旨在实现易开发、高效执行、全场景覆盖三大目标，提供支持异构加速的张量可微编程能力，支持云、服务器、边和端多种硬件平台；\n",
    "\n",
    "（3）Tensor模块主要用于数据储存；\n",
    "\n",
    "（4）context模块用于配置当前运行环境；\n",
    "\n",
    "#### 2.6 搭建量子神经网络"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "MindQuantumLayer<>"
     },
     "metadata": {},
     "execution_count": 10
    }
   ],
   "source": [
    "from mindquantum.nn import MindQuantumLayer        #从mindquantum.nn模块中（nn表示神经网络）导入MindQuantumLayer\n",
    "ms.set_seed(1)                                     #设置生成随机数的种子\n",
    "Quantumnet = MindQuantumLayer(encoder_names, ansatz_names, circuit, ham, n_threads=1)\n",
    "Quantumnet"
   ]
  },
  {
   "source": [
    "上述打印可以看到，我们已经成功搭建了量子机器学习层，其可以无缝地跟MindSpore中其它的算子构成一张更大的机器学习网络。\n",
    "\n",
    "说明：\n",
    "\n",
    "（1）MindQuantumLayer模块可以生成可训练的MindQuantum量子机器学习层，一般格式如下：MindQuantumLayer(encoder_params_names, ansatz_params_names, circuit, measurements, weight_init=\"normal\", n_threads=1)；\n",
    "\n",
    "（2）我们也可以通过如下代码方式搭建量子机器学习层，只是在MindQuantum中，已经将下述过程封装打包，这样我们就可以直接利用MindQuantumLayer模块搭建量子机器学习层。对于更复杂的量子-经典混合神经网络，如下搭建方式会展示它的优势（将在以后的tutorials中介绍）；"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "```python\n",
    "class MindQuantumLayer(nn.Cell):\n",
    "    def __init__(self,\n",
    "                 encoder_params_names,\n",
    "                 ansatz_params_names,\n",
    "                 circuit,\n",
    "                 measurements,\n",
    "                 weight_init='normal',\n",
    "                 n_threads=1):\n",
    "        super(MindQuantumLayer, self).__init__()\n",
    "        self.circuit = circuit\n",
    "        self.measurements = measurements\n",
    "        self.encoder_params_names = encoder_params_names\n",
    "        self.ansatz_params_names = ansatz_params_names\n",
    "        self.pqc = generate_pqc_operator(encoder_params_names,\n",
    "                                         ansatz_params_names,\n",
    "                                         circuit,\n",
    "                                         measurements,\n",
    "                                         n_threads=n_threads)\n",
    "        self.weight = Parameter(initializer(weight_init,\n",
    "                                            len(ansatz_params_names)),\n",
    "                                name=\"weight\")\n",
    "    def construct(self, x):\n",
    "        x, _, _ = self.pqc(x, self.weight)\n",
    "        return x\n",
    "```"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "#### 2.7 训练\n",
    "\n",
    "我们采用Adam优化器优化Ansatz中的参数。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stderr",
     "text": "[WARNING] DEBUG(6099,python):2021-08-30-08:09:15.881.718 [mindspore/ccsrc/debug/debugger/debugger.cc:79] Debugger] Not enabling debugger. Debugger does not support CPU.\n0 :  [[0.2837115]]\n10 :  [[-0.8851233]]\n20 :  [[-0.97001773]]\n30 :  [[-0.9929431]]\n40 :  [[-0.9939507]]\n50 :  [[-0.9967014]]\n60 :  [[-0.99878186]]\n70 :  [[-0.9995535]]\n80 :  [[-0.9999011]]\n90 :  [[-0.99998033]]\n100 :  [[-0.9999989]]\n110 :  [[-0.99999785]]\n120 :  [[-0.999997]]\n130 :  [[-0.9999987]]\n140 :  [[-0.9999998]]\n150 :  [[-1.]]\n160 :  [[-0.99999994]]\n170 :  [[-1.]]\n180 :  [[-1.]]\n190 :  [[-1.]]\n"
    }
   ],
   "source": [
    "from mindspore import nn                                          #从mindspore库导入nn模块，nn即经典神经网络\n",
    "from mindspore.nn import Adam, TrainOneStepCell                   #从mindspore.nn模块导入Adam模块和TrainOneStepCell模块\n",
    "\n",
    "opti = Adam(Quantumnet.trainable_params(), learning_rate = 0.5)   #需要优化的是Quantumnet中可训练的参数，学习率设为0.5   \n",
    "net = TrainOneStepCell(Quantumnet, opti)\n",
    "\n",
    "for i in range(200):\n",
    "    res = net(Tensor(encoder_data))\n",
    "    if i % 10 == 0:\n",
    "        print(i, ': ', res)"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，最后测量值收敛于-1。\n",
    "\n",
    "说明：\n",
    "\n",
    "（1）Adam模块通过自适应矩估计算法更新梯度，可以优化Ansazt中的参数，输入的是神经网络中可训练的参数；一般格式如下：nn.Adam(net.trainable_params(), learning_rate = 0.5)\n",
    "\n",
    "（2）TrainOneStepCell模块为网络训练包类，用优化器包装网络。生成的单元格使用输入“inputs”进行训练，将在构造函数中创建反向图，以更新参数，有不同的并行模式可用于训练。一般格式如下：nn.TrainOneStepCell(network, optimizer, sens=1.0)；\n",
    "\n",
    "#### 2.8 结果呈现\n",
    "\n",
    "由于测量值已经收敛于-1，所以我们可以打印此时Ansatz中的参数。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "[ 2.2420275 -1.0756909]\n"
    }
   ],
   "source": [
    "theta0, theta1 = Quantumnet.weight.asnumpy()\n",
    "print(Quantumnet.weight.asnumpy())"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，此时Ansatz中的参数$\\theta_1, \\theta_2$分别为2.2420275和-1.0756909。\n",
    "\n",
    "通过StateEvolution模块的final_state，可以输出量子线路在最优参数时的量子态。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "(0.37129759788513184-0.9285139441490173j)¦0⟩\n(1.4565440324076917e-05+6.52097298825538e-07j)¦1⟩\n"
    }
   ],
   "source": [
    "pr = {'alpha0': alpha0, 'alpha1': alpha1,'alpha2': alpha2,'theta0': theta0, 'theta1': theta1}\n",
    "state = StateEvolution(circuit).final_state(pr, ket = True)\n",
    "print(state)"
   ]
  },
  {
   "source": [
    "从上述打印可以看到，这就是量子线路在最优参数时的量子态。从其数值表示可以看到，这是一个接近于目标态$|0\\rangle$​​​的态。最后，我们计算一下此量子态与目标态$|0\\rangle$​​​​​的保真度（用于验证两个量子态的相似程度），并将保真度打印。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "1.0000000506744333\n"
    }
   ],
   "source": [
    "state = StateEvolution(circuit).final_state(pr)\n",
    "fid = np.abs(np.vdot(state, [1, 0]))**2            #保真度fidelity为向量内积的绝对值的模平方，即计算此时量子态对应的向量与|0>态对应的向量[1,0]的内积的模平方\n",
    "print(fid)"
   ]
  },
  {
   "source": [
    "\n",
    "可以看到，此时的保真度为100.00%，也就是说，该状态与目标态$|0\\rangle$​​的相似程度为100.00%。\n",
    "\n",
    "综上所述，我们搭建了一个简单的量子神经网络，通过训练Ansatz中的参数，抵消了Encoder对初始量子态产生的误差，使得最后的量子态仍为$|0\\rangle$​，且保真度达到100.00%。\n",
    "\n",
    "至此，我们通过MindQuantum完成了对量子神经网络的初体验！赶紧动手体验一下量子编程的乐趣吧！\n",
    "\n",
    "若想查询更多关于MindQuantum的API，请点击：[https://mindspore.cn/mindquantum/](https://mindspore.cn/mindquantum/)。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  }
 ]
}